//2.1.2
//tapeof
/* g=typeof window //将window的数据类型赋给g，若Window未被定义，则undefined。
 console.log(g)

 console.log(typeof window)//打印出window的数据类型，则object
*/


// console.log(typeof 123 )// "number"

// console.log(typeof '123') // "string"

// console.log(typeof false )// "boolean"

// function f() {}
// console.log(typeof f)// "function"

// console.log(typeof undefined)// "undefined"

// //typeof可以用来检查一个没有声明的变量，而不报错
// console.log(typeof v)// "undefined"

//console.log(typeof window )// "undefind",因为node.js没有Window，那是浏览器的东西。

// console.log(typeof {}) // "object"

// console.log(typeof [] )// "object"
//数组本质上只是一种特殊的对象,而instanceof运算符可以区分数组和对象。
// var o = {};
// var a = [];

//  console.log(o instanceof Array) // false
//  console.log(a instanceof Array)// true

// console.log(typeof null) // "object"

//2.2.1-1
//undefined或null语法效果几乎没区别。
//在if语句中，undefined和null都会被自动转为false，视为“假值”
// if (!undefined) {
//     console.log('undefined is false');
//   }
//   // undefined is false

//   if (!null) {
//     console.log('null is false');
//   }
//   // null is false

//   if (undefined == null){
//     console.log(true)
//   }
//   // true

//2.2.1-2
// // 变量声明了，但没有赋值
// var i;
// i // undefined

// 调用函数时，应该提供的参数没有提供，该参数等于 undefined
// function f(x) {
//   return x=1;
// }
// console.log(f(x))//报错,调用函数f并传递一个名为x的参数给它,但是x没有被定义（return不是定义）

// function f(x) {
//     return x;
//   }
//console.log(f()) // undefined,调用函数，没有明确指定返回值

// // 对象没有赋值的属性
// var  o = new Object();
// console.log(o.p) // undefined
// console.log(o.p="hello") //hello

// // 函数没有返回值时，默认返回 undefined
// function f() {}
// f() // undefined

// // 2.2.2
// if ('') {//如果条件表达式返回 true，则 if 语句的代码块会执行；如果条件表达式返回 false，则代码块将被跳过。
//     console.log('false');
//   }

// if ([]) {
//     console.log('true');
//   }
//   // true

// if ({}) {
//     console.log('true');
//   }
//   // true

//2.3.1-1
// if(1 === 1.0){
//     console.log(true)
// } // true

// 0.1 + 0.2 
// // false

// const result1 = 0.1 + 0.2;
// console.log(result1); // 打印结果

// const result2 = 1 + 2;
// console.log(result2); // 打印结果
// //四舍五入
// const sum = 0.1 + 0.2;
// console.log(sum.toFixed(1) === 0.3); // true

// const result3 = 0.3 / 0.1
// console.log(result3)
// // 2.9999999999999996

(0.3 - 0.2) === (0.2 - 0.1)
// false

// (0.3 - 0.2) === (0.2 - 0.1)
// // false

//2.3.1-2
//math.pow(基数，指数)
//精度最多只能到53个二进制位，这意味着，绝对值小于2的53次方的整数，即-2^53到2^53，都可以精确表示。

// console.log(Math.pow(2, 53))
// // 9007199254740992(精确表示)

// console.log(Math.pow(2, 53) + 1)
// // 9007199254740992（非）

// console.log(Math.pow(2, 53) + 2)
// // 9007199254740994（非），表面对，精度不对。
// //Math.pow(2, 53) 表示的是2的53次方，这是 JavaScript 中的最大整数精确表示。然而，当你将2添加到这个值时，结果会超出JavaScript可以精确表示的整数范围，从而导致精度丢失。

// console.log(Math.pow(2, 53) + 3)
// // 9007199254740996（非）

// console.log(Math.pow(2, 53) + 4)
// //9007199254740996（非）

//2.3.1-3
//JavaScript 能够表示的数值范围为21024到2-1023（开区间），超出这个范围的数无法表示。
// console.log(Math.pow(2, 1024)) 
// Infinity,一个数大于等于2的1024次方，那么就会发生“正向溢出”

// console.log(Math.pow(2, -1075) )
// 0,如果一个数小于等于2的-1075次方（指数部分最小值-1023，再加上小数部分的52位），那么就会发生为“负向溢出”

// var x = 0.5;

// for(var i = 0; i < 25; i++) {
//   x = x * x;
// }

// console.log(x) 
// 0，对0.5连续做25次平方，由于最后结果太接近0，超出了可表示的范围，JavaScript 就直接将其转为0。

//2.3.2
//小数点前的数字多于21位,JavaScript 会自动将数值转为科学计数法表示:
// console.log(1234567890123456789012)//22位
// 1.2345678901234568e+21

// console.log(123456789012345678901)//21位
// 123456789012345680000
//123456789012345678901 是一个非常大的整数，超出了 JavaScript 可以精确表示的整数范围，因此在数字的后面会出现一些零，这是精度问题的结果

// 小数点后紧跟5个以上的零，就自动转为科学计数法
// console.log(0.0000003) //6个零
// 3e-7

// 否则，就保持原来的字面形式
// console.log(0.000003 )//5个零
// 0.000003

//2.3.3
// console.log(0xff) // 255=15*16^0+15*16^1
// console.log(0o377) // 255=7*8^0+7*8^1+3*8^2
// console.log(0b11) // 3=1*2^0+1*2^1

//有前导0的数值会被视为八进制，但是如果前导0后面有数字8和9，则该数值被视为十进制。
// console.log(0888) // 888,十进制
// console.log(0777) // 511，八进制，=7*8^2+7*8^1+7*8^0

//2.3.4-1
//JavaScript 的64位浮点数之中，有一个二进制位是符号位。这意味着，任何一个数都有一个对应的负值，就连0也不例外。
// JavaScript 内部实际上存在2个0：一个是+0，一个是-0，区别就是64位浮点数表示法的符号位不同。它们是等价的
// if(-0 === +0 ){
//     console.log(true)
// }// true

// if(0 === -0 ){
//     console.log(true)
// }// true

// if(0 === +0){
//     console.log(true)
// } // true

//几乎所有场合，正零和负零都会被当作正常的0
// console.log(+0) // 0
// -0//0
// console.log(-0) // -0,当你使用console.log(-0)时，JavaScript会显示-0来表示负零，以帮助区分正零和负零。
// console.log((-0).toString()) // '0'
// console.log((+0).toString()) // '0'

//+0或-0当作分母，返回的值是不相等的。
// if((1 / +0) !== (1 / -0)){
//     console.log(true)
// }
//true,因为除以正零得到+Infinity，除以负零得到-Infinity,两者不相等.

//2.3.4-2
// console.log(5 - 'x') // NaN(非数字),因为字符串不能转换为数值

//一些数学函数的运算结果会出现NaN
// console.log(Math.acos(2)) // NaN
// console.log(Math.log(-1) )// NaN
// console.log(Math.sqrt(-1)) // NaN
// console.log(0/0)//NaN

// console.log(typeof NaN) // 'number'

//NaN不等于任何值，包括它本身。
// console.log(NaN === NaN) // false

//查找数组中某个元素的索引。在这种情况下，我们试图查找数组 [NaN] 中 NaN 元素的索引。
// console.log([NaN].indexOf(NaN)) // -1，不成立

// const arr = [NaN];
// const index = arr.findIndex(element => Number.isNaN(element));
// console.log(index); // 打印 0，找到了 NaN 的索引

//NaN在布尔运算时被当作false。
// console.log(Boolean(NaN)) // false

//NaN与任何数（包括它自己）的运算，得到的都是NaN。
// console.log(NaN + 1) // NaN

//2.3.4-3
// Infinity表示“无穷”,一种是一个正的数值太大，或一个负的数值太小，无法表示；另一种是非0数值除以0，得到Infinity。
// // 场景一
// Math.pow(2, 1024)
// // Infinity

// // 场景二
// 0 / 0 // NaN
// 1 / 0 // Infinity

//Infinity有正负之分，Infinity表示正的无穷，-Infinity表示负的无穷。
// console.log(Infinity === -Infinity) // false

// console.log(1 / -0 )// -Infinity
// console.log(-1 / -0) // Infinity

//Infinity大于一切数值（除了NaN），-Infinity小于一切数值（除了NaN）。
// console.log(Infinity > 10) // true
// console.log(-Infinity < 10) // true

//Infinity与NaN比较，总是返回false。
// console.log(Infinity > NaN) // false
// console.log(Infinity < NaN) // false
// console.log(Infinity == NaN) // false
// console.log(-Infinity > NaN) // false
// console.log(-Infinity < NaN) // false
// console.log(-Infinity == NaN) // false

//Infinity与它本身比较，返回true。
// console.log(Infinity > -Infinity) // false

//Infinity的四则运算，符合无穷的数学计算规则
// console.log(Infinity - Infinity) // NaN
// console.log(Infinity + Infinity) // Infinity
// console.log(Infinity * Infinity) // Infinity
// console.log(Infinity / Infinity) // NaN
// console.log(0/ Infinity) // 0
// console.log(0/ -Infinity) // -0
// console.log(0* Infinity) // NaN
// console.log(0* -Infinity)   // NaN
//Infinity与null计算时，null会转成0，等同于与0的计算。
//Infinity与undefined计算，返回的都是NaN。
// undefined + Infinity // NaN
// undefined - Infinity // NaN
// undefined * Infinity // NaN
// undefined / Infinity // NaN
// Infinity / undefined // NaN

//2.3.5-1
//parseInt方法用于将字符串转为整数。
// console.log(parseInt('123')) // 123

// 如果字符串头部有空格，空格会被自动去除
// console.log(parseInt(' 123')) // 123
// console.log(parseInt('123   456')) // 123
//当你传递一个包含非数字字符的字符串给 parseInt 时，它会尝试解析字符串的开始部分，直到遇到一个非数字字符或者空格。之后，它会忽略字符串中的空格。

// const str = '123   456';
// const numericStr = str.replace(/\D/g, ''); // 使用正则表达式去除非数字字符
// const result = parseInt(numericStr);
// console.log(result); // 打印 123456

//如果parseInt的参数不是字符串，则会先转为字符串再转换。
// parseInt(1.23) // 1
// // 等同于
// parseInt('1.23') // 1

//如果字符串的第一个字符不能转化为数字（后面跟着数字的正负号除外)（后面跟着的字符可以），返回NaN。
// parseInt('abc') // NaN
// parseInt('.3') // NaN
// parseInt('') // NaN
// parseInt('+') // NaN
// parseInt('+1') // 1
//依次转换，如果遇到不能转为数字的字符，就不再进行下去，返回已经转好的部分。
// parseInt('8a') // 8
// parseInt('12**') // 12
// parseInt('12.34') // 12
// parseInt('15e2') // 15
// parseInt('15px') // 15

//parseInt的返回值只有两种可能，要么是一个十进制整数，要么是NaN。
// console.log(parseInt('0x10')) // 16, 如果字符串以0x或0X开头，parseInt会将其按照十六进制数解析。
// console.log(parseInt('011') )// 11,如果字符串以0开头，将其按照10进制解析。



// // TODO： 小贾同学，你可以这样写，把你要测试的代码封装成一个函数，然后调用这个函数，这样就可以需要的时候调用，也不用注释那么多了，例如这样：

// // 这是标准的注释方法，可以参考一下
// /**
//  * 拼接两个字符串
//  * @param { string } str1 参数1
//  * @param { string } str2 参数2
//  * @remark 这里是注释的内容（没有就省略）
//  * @returns { string } 返回字符串（没有就省略）
//  */
// function concatString(str1, str2) {
//   const res = str1 + str2;
//   console.log(res);
//   return res
// }

// // 在方法名称前面使用多行注释，当你把鼠标移动到函数上的时候，就可以看到注释了
// /**
//  * 拼接不限数量的字符串
//  * @returns {string} 返回字符串
//  */
// function concatStringWithoutLimits() {
//   let res = '';
//   for (const item of arguments) {
//     res += item;
//   }
//   console.log(res);
//   return res
// }

// // 另外，每个方法之间都要间隔一行，这样看起来比较清晰美观

// // TODO：测试代码集中到一个地方
// concatString('hello', 'world'); // helloworld
// concatStringWithoutLimits('hello', '', 'world', ',', 'this', ' is', ' test'); // helloworld

//2.3.5-2
/**
 * @param {string} str1
 * @remark parseFloat方法用于将一个字符串转为浮点数
 */
function first(str1) {
  str1='3.14';
  return parseFloat(str1);
}

/**
 * @param {string} str2
 * @remark 如果字符串符合科学计数法，则会进行相应的转换
 */
function second(str2) {
  str2='314e-2';
  return parseFloat(str2);
}

/**
 * @param {string} str3
 * @remark 如果字符串中包含不能转为浮点数的字符，则不再进行往后转换
 */
function third(str3) {
  str3='3.14abc';
  return parseFloat(str3);
}

/**
 * @param {string} str4
 * @remark parseFloat方法会自动过滤字符串前导的空格。
 */
function fourth(str4) {
  str4='\t\v\r12.34\n ';
  return parseFloat(str4);
}

/**
 * @param {string} str5
 * @remark 如果参数不是字符串，则会先转为字符串再转换。
 */
function fifth(str5) {
  str5=[1.23];
  return parseFloat(str5);
}

/**
 * @param {string} str6
 * @remark 如果字符串的第一个字符不能转化为浮点数，则返回NaN。
 */
function sixth(str6) {
  str6='FF2';
  return parseFloat(str6);
}

/**
 * @param {string} str7
 * @remark parseFloat会将空字符串转为NaN
 */
function seventh(str7) {
  str7='';
  return parseFloat(str7);
}

/**
 * @param {string} str71
 * @param {string} str72
 * @param {string} str73
 * @remark parseFloat的转换结果不同于Number函数
 */
function eighthpointone(str71,str72,str73) {
  str71=true;
  str72=null;
  str73="123.45#";
  return{
    return1:parseFloat(str71),
    return11:Number(str71),
    return2:parseFloat(str72),
    return21:Number(str72),
    return3:parseFloat(str73),
    return31:Number(str73)
  };  
}

// console.log(first());// 3.14
// console.log(second());// 3.14
// console.log(third());// 3.14
// console.log(fourth());// 12.34
// console.log(fifth());// 1.23
// console.log(sixth());// NaN
// console.log(seventh());// NaN
// console.log(eighthpointone());
/*
{
  return1: NaN,
  return11: 1,
  return2: NaN,
  return21: 0,
  return3: 123.45,
  return31: NaN
}*/


//2.3.5-3
/** 
 * @param {string} str8
 * @param {string} str81
 * @remark isNaN方法可以用来判断一个值是否为NaN
 */
function first1(str8,str81) {
  str8=123;
  str81=NaN;
  return{ 
  return1:isNaN(str8),
  return2:isNaN(str81)
  };
}

/**
 * @param {string} str9
 * @remark isNaN为true的值，有可能不是NaN，而是一个字符串。因为传入字符串的时候，字符串会被先转成NaN，所以最后返回true。
 */
function second1(str9) {
  str9='hello';
  return isNaN(str9);
}

/**
 *@param {string} str10
 *@param {string} str101
 *@remark 出于同样的原因，对于对象和数组，isNaN也返回true。
 *@remark ['xzy'] 表达的是一个包含单个字符串元素 'xzy' 的数组.
 *@remark 数组转换为字符串时，字符串 'xzy' 不能被转换为数字，所以 isNaN 返回 true
 */
function third1(str10,str101) {
  str10={};
  str101=['xyz'];
  return{ 
    return1:isNaN(str10),
    return2:isNaN(str101)
  };
}

/**
 * @param {string} str11
 * @param {string} str111
 * @remark 对于空数组和只有一个数值成员的数组，isNaN返回false。
 * @remark [234]表示一个包含单个整数元素 234 的数组;[2, 3, 4] 表示包含三个整数元素的数组，分别是 2、3 和 4
 * @remark 数组转换为字符串时，字符串 '234' 可以被转换为数字，所以 isNaN 返回 false
 */
function fourth1(str11,str111) {
  str11=[];
  str111=[234];
  return{ 
    return1:isNaN(str11),
    return2:isNaN(str111)
  };
}

/**
 * @param {string} str12
 * @remark 使用isNaN之前，最好判断一下数据类型
 * @remark 表示同时检查 str12 是否是一个数字（数字类型）并且不是 NaN（非数字）
 */
function fifth1(str12) {
  str12=NaN;
  if (typeof str12 === 'number' && !isNaN(str12)) {
    return true;
  } else {
    return false;
  }
}

/**
 * @param {string} str13
 * @remark 判断NaN更可靠的方法是，利用NaN为唯一不等于自身的值的这个特点，进行判断。
 */
function sixth1(str13) {
  str13=NaN;
  return str13 !== str13;
}

// console.log(first1());// {return1: false, return2: true}
// console.log(second1());// true
// console.log(third1());// {return1: true, return2: true}
// console.log(fourth1());// {return1: false, return2: false}
// console.log(fifth1());//false
// console.log(sixth1());//true


//2.3.5-4
/**
 * @param {string} str14
 * @param {string} str141
 * @param {string} str142
 * @param {string} str143
 * @param {string} str144
 * @remark isFinite方法返回一个布尔值，表示某个值是否为正常的数值。
 */
function first2(str14,str141,str142,str143,str144) {
  str14=Infinity;
  str141=-Infinity;
  str142=NaN;
  str143=undefined;
  str144=null;
  return{ 
    return1:isFinite(str14),
    return2:isFinite(str141),
    return3:isFinite(str142),
    return4:isFinite(str143),
    return5:isFinite(str144)
  };
}

// console.log(first2());
/*
{
  return1: false,
  return2: false,
  return3: false,
  return4: false,
  return5: true
}*/

//2.4.1-1
/**
 * @param {string} str15
 * @remark 要在单引号字符串的内部，使用单引号，就必须在内部的单引号前面加上反斜杠，用来转义。
 */
function first3(str15) {
  str15='Did she say \'Hello\'?'  ;
  return str15;
}

/**
 * @param {string} str16
 * @remark 字符串默认只能写在一行内，分成多行将会报错。
 */
// function second3(str16) {
//   str16='a
//   b
//   c'
//   ;
//   return str16;
// }

/**
 * @param
 * @remark 长字符串必须分成多行，可以在每一行的尾部使用反斜杠
 */
function third3(str17) {
  str17='Did she say \'Hello\'? \
  Yes, she did. \
  She said it twice.'
  ;
  return str17;
}

// console.log(first3());//Did she say 'Hello'?
// console.log(second3());//SyntaxError: Invalid or unexpected token
// console.log(third3());//Did she say 'Hello'? Yes, she did. She said it twice.

//2.4.1-2
/**
 * @param {string} str18
 * @remark 字符串中可以包含换行符，用 \n 表示。
 */
function first4(str18) {
  str18='This is the first line.\nAnd this is the second line.'
  ;
  return str18;
}

/**
 * @param {string} str19
 * @remark 在非特殊字符前面使用反斜杠，则反斜杠会被省略。
 */
function second4(str19) {
  str19='\\a'
  ;
  return str19;
}

/**
 * @param {string} str20
 * @remark 字符串的正常内容之中，需要包含反斜杠，则反斜杠前面需要再加一个反斜杠，用来对自身转义。
 */
function third4(str20) {
  str20='\\\\a'
  ;
  return str20;
}

// console.log(first4());
//This is the first line.
// And this is the second line.
// console.log(second4());//\a
// console.log(third4());//\\a

//2.4.1-3
/**
 * @param {string} str21
 * @remark 数组
 * @remark 方括号中的数字超过字符串的长度，或者方括号中根本不是数字，则返回undefined。
 */
function first5(str21) {
  str21=['a','b','c']
  ;
  return {
    return1:str21[0],
    return2:str21[4]
    };
}

// console.log(first5());//{ return1: 'a', return2: undefined }

//2.4.1-4
/**
 * @param {string} str22
 * @remark length属性返回字符串的长度
 */
function first6(str22) {
  str22='abc';
  return str22.length;
}

// console.log(first6());//3

//2.4.2
/**
 * @param {string} str23
 * @remark 字符集(版权符号)
 */
function first7(str23) {
  str23='\u00A9';
  return str23;
}

// console.log(first7());//©

//2.4.3
/**
 * @param {string} str24
 * @remark btoa()：任意值转为 Base64 编码
 */
function first8(str24) {
  str24='Hello';
  return btoa(str24);
}

/**
 * @param {string} str25
 * @remark atob()：Base64 编码转为任意值
 */
function second8(str25) {
  str25='SGVsbG8=';
  return atob(str25);
}

/**
 * @param {string} str26
 *@remark 这两个方法不适合非 ASCII 码的字符，会报错
 */
function third8(str26) {
  str26='你好';
  return btoa(str26);
}

/**
 * @param {string} str27
 * @remark 要将非 ASCII 码字符转为 Base64 编码，必须中间插入一个转码环节，再使用这两个方法。
 */
function fourth8(str27) {
  str27='你好';
  return btoa(encodeURIComponent(str27));
}

// console.log(first8());//SGVsbG8=
// console.log(second8());//Hello
// console.log(third8());//Uncaught DOMException: The string to be encoded contains characters outside of the Latin1 range.
// console.log(fourth8());//JUU0JUJEJUEwJUU1JUE1JUJE

//2.5.1-1
/**
 * @param {string} str28
 * @remark 对象就是一组“键值对”（key-value）的集合，是一种无序的复合数据集合。
 */
function first9(str28) {
  str28={
    foo: 'Hello',
    bar: 'World'
    };
  return str28;
}

//console.log(first9());//{ foo: 'Hello', bar: 'World' }

//2.5.1-2
/**
 * @param {string} str29
 * @remark 对象的每一个键名又称为“属性”（property），它的“键值”可以是任何数据类型。
 */
function second9(str29) {
  str29={
    p: function (x) {
      return 2 * x;
    }  
  };
  return str29.p(3);
}

/**
 * @param {string} str30
 * @remark 如果属性的值还是一个对象，就形成了链式引用。
 */
function third9(str30) {
  var o1 = {};
  var o2 = { bar: 'hello' };
  
  o1.foo = o2;
  return o1.foo.bar
}

/**
 * @param {string} str31
 * @remark 属性可以动态创建，不必在对象声明时就指定。
 */
function fourth9(str31) {
  var o = {};
  o.foo = 123;
  return o.foo;
}

// console.log(second9());//6
// console.log(third9());//hello
// console.log(fourth9());//123

//2.5.1-3
/**
 * @param {string} str32
 * @remark 如果不同的变量名指向同一个对象，那么它们都是这个对象的引用，也就是说指向同一个内存地址。
 * @param {string} str321
 * @remark 修改其中一个变量，会影响到其他所有变量。
 * @param {string} str322
 * @remark 此时，如果取消某一个变量对于原对象的引用，不会影响到另一个变量。
 * @param {string} str323
 * @remark 但是，这种引用只局限于对象，如果两个变量指向同一个原始类型的值。那么，变量这时都是值的拷贝。
 */
function first10(str32) {
  var o1 = {};
  var o2 = o1;
  
  o1.a = 1;  
  return o2.a
}
function second10(str321) {
  var o1 = {};
  var o2 = o1;
  
  o2.b = 2;
  return o1.b
}
function third10(str322) {
  var o1 = {};
  var o2 = o1;
  
  o1 = 1;
  return o2
}

function fourth10(str323) {
  var x = 1;
  var y = x;
  x = 2;
  return y
}

// console.log(first10());//1
// console.log(second10());//2
// console.log(third10());//{}
// console.log(fourth10());//1

//2.5.1-4
/**
 * @param {string} str33
 * @remark 圆括号的里面，只能是表达式，所以确保大括号只能解释为对象。
 */
function first11(str33) {
  a=eval('{foo: 123}') 
  b=eval('({foo: 123})')
  return{
    return1:a,
    return2:b
  }
}

// console.log(first11());//{ return1: undefined, return2: { foo: 123 } }

//2.5.2-1
/**
 * @param {string} str34
 * @remark 读取对象的属性，有两种方法，一种是使用点运算符，还有一种是使用方括号运算符。
 * @remark 引用对象obj的foo属性时，如果使用点运算符，foo就是字符串；如果使用方括号运算符，但是不使用引号，那么foo就是一个变量，指向字符串bar
 */
function first12(str34) {
  var foo = 'bar';
  var obj = {
    foo: 1,
    bar: 2
  };
  
  return{
    return1:obj.foo,
    return2:obj[foo]
  }
}

/**
 *@param {string} str35
 *@remark 方括号运算符内部还可以使用表达式
 */
function second12(str35) {
  var obj = {};
  obj [2+2] = 5; 
  return obj[2+2]
}

/**
 * @param {string} str36
 * @remark 数值键名不能使用点运算符(因为会被当成小数点),只能使用方括号运算符
 */
function third12(str36) {
  var obj = {};
  obj[2] = 5;
  // return obj.2
}

// console.log(first12());//{ return1: 1, return2: 2 }
// console.log(second12());//5
// console.log(third12());//SyntaxError: Unexpected number

//2.5.2-2
/**
 * @param {string} str37
 * @remark 点运算符和方括号运算符，不仅可以用来读取值，还可以用来赋值。
 */
function first13(str37) {
  var obj = {};
  return{
  return1:obj.foo = 'Hello',
  return2:obj['bar'] = 'World'
  }
} 

// console.log(first13());//{ return1: 'Hello', return2: 'World' }

//2.5.2-3
/**
 * @param {string} str38
 * @remark 查看一个对象本身的所有属性，可以使用Object.keys方法。
 */
function first14(str38) {
  var obj = {
    key1: 1,
    key2: 2
  };
  
  return Object.keys(obj);
}
// console.log(first14());//[ 'key1', 'key2' ]

//2.5.2-4
/**
 * @param {string} str39
 * @remark delete命令用于删除对象的属性，删除成功后返回true。
 */
function first15(str39) {
  var obj = { p: 1 };
  
  return Object.keys(obj)
}

function first16(str39) {
  var obj = { p: 1 };
  
  delete obj.p;
  return Object.keys(obj);
}
function first17(str39) {
  var obj = { p: 1 };
  
  return delete obj.p;
}

/**
 * @param {string} str40
 * @remark 只有一种情况，delete命令会返回false，那就是该属性存在，且不得删除。
 */
function first18(str40) {
  var obj = Object.defineProperty({}, 'p', {
    value: 123,
    configurable: false
  });
  
  return delete obj.p 
}

/**
 * @param {string} str41
 * @remark delete命令只能删除对象本身的属性，无法删除继承的属性
 */
function first19(str41) {
  var obj = {};
  return delete Object.prototype; 
}

// console.log(first15());//[ 'p' ]
// console.log(first16());//[]
// console.log(first17());//true
// console.log(first18())//false
// console.log(first19());//false

//2.5.2-5
/**
 * @param {string} str42
 * @remark in运算符用于检查对象是否包含某个属性（注意，检查的是键名，不是键值），如果包含就返回true，否则返回false。
 */
function first20(str42) {
  var obj = { p: 1 };
  return 'p' in obj;
}
/**
 * @param {string} str43
 * @remark 可以使用对象的hasOwnProperty方法判断一下，是否为对象自身的属性。
 */
function first21(str43) {
  var obj = {};
  obj.p = 1;
  return obj.hasOwnProperty('tostring');
}
// console.log(first/20());//true
// console.log(first21())//false

//2.5.2-6
/**
 * @param {string} str44
 * @remark for...in循环用来遍历一个对象的全部属性。
 * @param {string} str45
 * @remark 对象obj继承了toString属性，该属性不会被for...in循环遍历到，因为它默认是“不可遍历”的。
 */
function first22(str44) {
  var obj = {a: 1, b: 2, c: 3};
  var result = '';

  for (var i in obj) {
    result += '键名： ' + i + '\n';
    result += '键值： ' + obj[i] + '\n';
  }

  return result;
}

function first23(str45) {
  var obj = {};
  var result= '';
  for (var p in obj) {
    result += true;
  }
  return result;
}

/**
 * @param {string} str46
 * @remark 使用for...in的时候，应该结合使用hasOwnProperty方法，在循环内部判断一下，某个属性是否为对象自身的属性。
 */

function first24(str46) {
  var result = '';
  var person = { name: '老张' };

  for (var key in person) {
    if (person.hasOwnProperty(key)) {
      result += key;
    }
  }
  return result;
}
// console.log(first22());
// 键名： a
// 键值： 1
// 键名： b
// 键值： 2
// 键名： c
// 键值： 3
// console.log(first23());//无输出
// console.log(first24());//name

//2.5.3
/**
 * @param {string} str47
 * @remark with语句
 */
function first25(str47) {
  var result = '';
  with (document.links[0]){
    result +=console.log(href);
    result +=console.log(title);
    result +=console.log(style);
  }
  return result;
}

/**
 * @param {string} str48
 * @remark with区块内部有变量的赋值操作，必须是当前对象已经存在的属性，否则会创造一个当前作用域的全局变量。
 */
function first26(str48) {
  var obj = {};
  with (obj) {
    p1 = 4;
    p2 = 5;
  }
  return{
    result1: obj.p1,
    result2: p1
  }
}

// console.log(first25());//报错，在Node.js中运行这段代码会出错是因为with语句在严格模式下是不允许的，并且document对象是浏览器环境的一个全局对象，在Node.js中并不存在。
// console.log(first26());//{ result1: 4, result2: undefined }

//2.6.1-1
/**
 * @param {string} str49
 * @param {string} str50
 * @remark 函数声明第一法与第三法
 */
function first27(str49) {
  var i=1
  if (i=1){
    console.log("全世界我最漂亮,我最可爱");
  }
}

function first28(str50) {
  return "全世界我最漂亮,我最可爱";
}

// first27()
// console.log(first28());

//2.6.1-2
/**
 * @param {string} str51
 * @remark 如果同一个函数被多次声明，后面的声明就会覆盖前面的声明。
 */
function first29(str51) {
  console.log(1);
}
// first29();//2
function first29(str51) {
  console.log(2);
}
// first29();//2

//2.6.1-3
/**
 * @param {string} str52,str53
 * @remark 调用函数时，要使用圆括号运算符。圆括号之中，可以加入函数的参数。
 */
function first30(str52,str53) {
  return str52+str53;
}

/**
 * @param {string} num
 * @remark 函数可以调用自身，这就是递归（recursion）。下面就是通过递归，计算斐波那契数列的代码。
 */
function fib(num) {
  if (num === 0) return 0;
  if (num === 1) return 1;
  return fib(num - 2) + fib(num - 1);
}

// console.log(first30(1,4))//5
// console.log(fib(10))//55

//2.6.1-4
/**
 * @param {string} str54
 * @remark 第一等公民：函数本身。
 */
function first31(str54) {
  function add(x, y) {
    return x + y;
  }

  var op= add;

  function a(op){
    return op;
  }

  return a(add)(1,1)
}

// console.log(first31())//2

//2.6.1-5
/**
 * @param {string} str55
 * @remark JavaScript 引擎将函数名视同变量名，所以采用function命令声明函数时，整个函数会像变量声明一样，被提升到代码头部。
 * @remark 上述代码等价于var f;f();f = function () {};
 */
function first32(str55) {
  f();  
  var f = function (){};
}

/**
 * @param {string} str56
 * @remark 由于变量提升，相当于先function f() {console.log('2');}再var f=function () {console.log('1');}覆盖后就是如下结果
 */
function first33(str56) {
  var f = function() {
    console.log('1');
  }
  
  function f() {
    console.log('2');
  }
  
  return f();
}

// console.log(first32())//报错
// console.log(()); //1;undefined

/**
 * @param {string} str561
 * @remark 拓展
 */
function first331(str561) {
  console.log('>>>print', 1)

  var f = function () {
    console.log('>>>print', 2)
    return 2
  }
  
  console.log('>>>print', 3, f())
  
  function f() {
    console.log('>>>>>print', 4)
    return 4
  }
  
  console.log('>>>print', 5, f())
  
  return f()
}
  
// console.log('>>>print', 6)
// console.log(first331())
// console.log('>>>print', 7)

//2.6.2-1
/**
 * @param {string} str57
 * @remark 函数的name属性返回函数的名字
 * @param {string} str58
 * @remark 如果是通过变量赋值定义的函数，那么name属性返回变量名。
 * @param {string} str59
 * @remark 只有在变量的值是一个匿名函数时才是如此。如果变量的值是一个具名函数，那么name属性返回function关键字之后的那个函数名。
 */
function first34(str57) {
  function f() {}
  return f.name;
}
function first35(str58) {
  var f = function () {};
  return f.name;
}
function first36(str59) {
  var f = function g() {};
  return f.name;
}
// console.log(first34())//f
// console.log(first35())//f
// console.log(first36())//g

//2.6.2-2
/**
 * @param {string} str60
 * @remark length属性返回函数预期传入的参数个数，即函数定义之中的参数个数。
 */
function first37(str60) {
  function f(a, b) {}
  return f.length 
}
// console.log(first37())//2

//2.6.2-3
/**
 * @param {string} str61
 * @remark 函数的toString()方法返回一个字符串，内容是函数的源码。
 */
function first38(str61) {
  function f() {
    a();
    b();
    c();
  }
  return f.toString()
}

/**
 * @param {string} str62
 * @remark 对于那些原生的函数，toString()方法返回function (){[native code]}。
 */
function first39(str62) {
  return Math.abs.toString()
}

/**
 * @param {string} str63
 * @remark 函数内部的注释也可以返回。
 */
function first40(str63) {
  function f() {
    //comment1
    a();
    /*comment2*/b();
    c();
    /*
    * comment3
    */
  }
  return f.toString()
}

// console.log(first38())
/*function f() {
 a();
 b();
 c();
}*/
// console.log(first39())//function abs() { [native code] }
// console.log(first40())
// function f() {
//   //comment1
//   a();
//   /*comment2*/b();
//   c();
//   /*
//   * comment3
//   */
// }

//2.6.3-1
/**
 * @param {string} str64
 * @remark 函数内部定义的变量，会在该作用域内覆盖同名全局变量。
 */
function first41(str64) {
  var v = 1;
  function f(){
    var v = 2;
    return v;
  }
  return {
    return1:f(),
    return2:v
  }
}
/**
 * @param {string} str65
 * @remark 对于var命令来说，局部变量只能在函数内部声明，在其他区块中声明，一律都是全局变量。
 */
function first42(str65) {
  if (true) {
    var x = 5;
  }
  return x;  
}

// console.log(first41())//{return1: 2, return2: 1}
// console.log(first42())//5

//2.6.3-2
/**
 * @param {string} str66
 * @param {string} str661
 * @remark var命令声明的变量，不管在什么位置，变量声明都会被提升到函数体的头部
 */
function first43(str66) {
  function foo(x) {
    var result;
    if (x > 100) {
      var tmp = x - 100;
      result =console.log(true);
    } else {
      result =console.log(false);
    }
    return result; 
  }
  return foo(200)
}

// console.log(first43()); // 输出 true,undefined(因为console.log(true)只打印，不return，result与console.log(true)不建立联系）
//在内部强加conslo.log,去不掉undefined，试过了。
//return console.log(a) 会返回 undefined，因为 console.log() 不会返回任何有意义的值。


function first431(str661) {
  function second(x) {
    if (x > 100) {
      var tmp = x - 100;
      return true
  }
    else{ 
      return false;
    }
  }
  return second(200);
}
// console.log(first431())//true

//2.6.3-3
/**
 * @param {string} str67
 * @remark 它的作用域与变量一样，就是其声明时所在的作用域，与其运行时所在的作用域无关。
 */
function first44(str67) {
  var a = 1;
  var x = function () {
    return a;
  };  
  function f() {
    var a = 2;
    return x();
  }
  return f();
}
/**
 * @param {string} str68
 * @remark x在声明域时，没有定义a的值，所以y(x)=y(a)=y(undefined).
 */
function first45(str68) {
  var x = function () {
    return a;
  };
  
  function y(f) {
    var a = 2;
    return f();
  }
  
  return y(x)
  
}

/**
 * @param {string} str69
 * @remark f()=foo()=bar()=x=1,与x=2无关。
 */
function first46(str69) {
  function foo() {
    var x = 1;
    function bar() {
      return x;
    }
    return bar;
  }
  
  var x = 2;
  var f = foo();
  return f() // 1
}
// console.log(first44())//1
// console.log(first45())// a is not defined
// console.log(first46())//1

//2.6.3-4-1
/**
 * @param {string} str70
 * @remark 参数
 */
function first47(str70) {
  function square(x) {
    return x * x;
  }
  return square(3) 
}
// console.log(first47())//9

//2.6.4-2
/**
 * @param {string} str71
 * @remark 参数省略,运行时无论提供多少个参数（或者不提供参数），JavaScript 都不会报错。
 */
function first48(str71) {
  function f(a, b) {
    return a;
  }
  return f(1,2,3,4) 
}
// console.log(first48())//1

//2.6.4-3
/**
 * @param {string} str72
 * @remark 传递参数----值传递,function f()的a与外的a无关。
 */
function first49(str72) {
  var a = 1;
  function f() {
    var a=3;
  }
  f(a);
  return a;
}

/**
 * @param {string} str73
 * @remark 传递参数----传址传递,function f()的a与外的a相关。
 */
function first50(str73) {
  var obj = { p: 1 };
  function f(o) {
    o.p = 2;
  }
  f(obj);
  
  return obj.p
}

/**
 * @param {string} str74
 * @remark 如果函数内部修改的，不是参数对象的某个属性，而是替换掉整个参数，这时不会影响到原始值
 */
function first51(str74) {
  var obj = [1, 2, 3];

  function f(o) {
    o = [2, 3, 4];
  }
  f(obj);
  
  return obj 
  
}
// console.log(first49())//1
// console.log(first50())//2
// console.log(first51())//[1,2,3]

//2.6.4-4
/**
 * @param {string} str75
 * @remark 如果有同名的参数，则取最后出现的那个值。
 */
function first52(str75) {
  function f(a, a) {
    return a;
  }
  return {
    return1: f(1,2),
    return2: f(1)
  }
}

// console.log(first52())//{ return1: 2, return2: undefined }

//2.6.4-5
/**
 * @param {string} str76
 * @remark arguments对象
 */
function first53(str76) {
  
  function f() {
    return arguments[0];
  }
  return f(1,2,3,4);
}

/**
 * @param {string} str77
 * @remark 正常模式下，arguments对象可以在运行时修改。
 */
function first54(str77) {
  var f = function(a, b) {
    arguments[0] = 3;
    arguments[1] = 2;
    return a + b;
  }
  
  return{ 
    return1:f() ,
    return2:f(1),
    return3:f(1,2)
  }
}

/**
 * @param {string} str78
 * @remark 严格模式下，arguments对象不能修改。
 */
function first55(str78) {
  'use strict';
  var f = function(a, b) {
    arguments[0] = 3;
    arguments[1] = 2;
    return a + b;
  }
  return{ 
    return1:f() ,
    return2:f(1),
    return3:f(1,2)
  }
}

/**
 * @param {string} str79
 * @remark 通过arguments对象的length属性，可以判断函数调用时到底带几个参数。
 */
function first56(str79) {
  var f = function() {
    return arguments.length;
  }
  return{ 
    return1:f(),
    return2:f(1),
    return3:f(1,2)
  }
}

/**
 * @param {string} str80
 * @param {string} str81
 * @remark arguments对象与数组转换----slice方法和逐一填入新数组。
 */
function first57(str80) {
  function f(a, b) {
    var args = Array.prototype.slice.call(arguments);
    return args;
  }
  return f(1,2,3);
}
function first58(str81) {
  function f(a, b) {
    var args = [];
    for(var i = 0;i < arguments.length;i++) {
      args.push(arguments[i]);
    }
    return args;
  }
  return f(0,2,3);
}

/**
 * @param {string} str82
 * @remark callee 属性,返回它所对应的原函数。
 */

function first59(str82) {
  var g = function() {
    return arguments.callee;
  }
  return g(1,2);
}
// console.log(first53())//1
// console.log(first54())//{ return1: NaN, return2: NaN, return3: 5 }
// console.log(first55())//{ return1: NaN, return2: NaN, return3: 3 }
// console.log(first56())//{ return1: 0, return2: 1, return3: 2 }
// console.log(first57())//[ 1, 2, 3 ]
// console.log(first58())//[ 0, 2, 3 ]
// console.log(first59())//[Function: g]

//2.6.5-1
/**
 * @param {string} str83
 * @remark 父对象的所有变量，对子对象都是可见的，反之则不成立。
 * @remark 只要把f2作为返回值，我们不就可以在f1外部读取它的内部变量了吗！
 */
function first60(str83) {
  function f1() {
    var n = 999;
    function f2() {
      return n;
    }
    return f2();
  }
  var result =f1();
  return result;
  //等价 return f1()
}

/**
 * @param {string} str84
 * @remark 闭包使得内部变量记住上一次调用时的运算结果。
 */
function first61(str84) {
  function createIncrementor(start) {
    return function () {
      return start++;
    };
  }
  
  var inc = createIncrementor(5);
  return {
    return1:inc(),
    return2:inc(),
    return3:inc()
  }
}

/**
 * @param {string} str85
 * @remark 闭包的另一个用处，是封装对象的私有属性和私有方法
 * @remark name是参数，而非函数，故name:p1.name,而不是name:p1.name().
 */
function first62(str85) {
  function Person(name) {
    var _age;
    function setAge(n) {
      _age = n;
    }
    function getAge() {
      return _age;
    }
  
    return {
      name: name,
      getAge: getAge,
      setAge: setAge
    };
  }
  
  var p1 = Person('张三');
  p1.setAge(25);
  return {
    name:p1.name,
    getAge: p1.getAge()
  }
}
// console.log(first60())//999
// console.log(first61())//{ return1: 5, return2: 6, return3: 7 }
// console.log(first62())//{ name: '张三', getAge: 25 }

//2.6.5-2
/**
 * @param {string} str86
 * @remark 立即执行失误，function这个关键字既可以当作语句，也可以当作表达式。
 */
// function first63(str86) {
//   function f(){ /* code */ }();
// }

/**
 * @param {string} str87
 * @remark 当作表达式时，函数可以定义后直接加圆括号调用。
 * @remark 在node.js必须要使用conosle.log()才会有结果。var f = function (){ return 1}();console.log(f)
 */
function first64(str87) {
  var f = function f(){
    return 1;
  }();
  return f;
}

// console.log(first63())//Unexpected token ')'
// console.log(first64())//1

//2.6.6-1(不用管)
/**
 * @param {string} str88
 * @remark eval命令接受一个字符串作为参数，并将这个字符串当作语句执行。
 */
function first65(str88) {
  eval('var a = 1;');
  return a;
}

/**
 * @param {string} str89
 * @remark 如果参数字符串无法当作语句运行，那么就会报错。
 */
// function first66(str89) {
//   eval('a=3x');
//   return a;
// }

/**
 * @param {string} str90
 * @remark 如果eval的参数不是字符串，那么会原样返回。eval(123) // 123
 * @remark eval没有自己的作用域，都在当前作用域内执行，因此可能会修改当前作用域的变量的值，造成安全问题。
 */
function first67(str90) {
  var a = 123;
  eval('a=1;');
  return a;
}

// console.log(first65())//1
// console.log(first66())//SyntaxError: Invalid or unexpected token
// console.log(first67())//1

//2.6.6-2
/**
 * @param {string} str91
 * @remark eval别名调用,凡是使用别名执行eval，eval内部一律是全局作用域。
 * @remark 由于eval执行的代码在其被调用的上下文中运行，而上下文是全局作用域，它会查找全局作用域中的变量a，而不是函数f内部的变量a。在全局作用域中，a的值是1，
 *@remark 使用别名，则报错，eval作为方法，不能赋给变量
 *@remark 以下代码，return eval(script)没有执行，出现undefined，但是不报错。
 */
function first68(str91) {
  var a = 1;
  function f() {
    var a = 2;
    var script = 'console.log(`>>>>>`,a - 5)';
    return eval(script)
  }
  return f();
}
// console.log(first68())//>>>>> -3   undefined

//2.7.1
/**
 * @param {string} str92
 * @remark 任何类型的数据，都可以放入数组
 * @remark 在 JavaScript 中，如果你尝试将一个函数对象直接打印或输出为字符串，它通常会以 [Function (anonymous)] 的形式显示。这是因为 JavaScript 的函数对象默认没有一个直观的字符串表示形式。
 */
function first69(str92) {
  var arr = [
    {a: 1},
    [1, 2, 3],
    function() {return true;}
  ];
  return{
  return1:arr[0] ,
  return2:arr[1] ,
  return3:arr[2]
  }
}

/**
 * @param {string} str93
 * @remark 如果数组的元素还是数组，就形成了多维数组。
 */
function first70(str93) {
  var a = [[1, 2], [3, 4]];
  return{
  retrun1: a[0][1] ,
  return2:a[1][1]
  }
}
// console.log(first69())
/*
{
  return1: { a: 1 },
  return2: [ 1, 2, 3 ],
  return3: [Function (anonymous)]
}*/
// console.log(first70())//{ retrun1: 2, return2: 4 }

//2.7.2
/**
 * @param {string} str94
 * @remark 数组属于一种特殊的对象。typeof运算符会返回数组的类型是object。
 */
function first71(str94) {
  var arr = [1, 2];
  return typeof arr;
}
/**
 * @param {string} str95
 * @remark Object.keys方法返回数组的所有键名
 * @remark 由于数组成员的键名是固定的（默认总是0、1、2...），因此数组不用为每个元素指定键名，而对象的每个成员都必须指定键名。
 */
function first72(str95) {
  var arr = [1, 2];
  return Object.keys(arr);
}

/**
 * @param {string} str96
 * @remark 一个值总是先转成字符串，再作为键名进行赋值。
 */
function first73(str96) {
  var a = [];
  a[1.00] = 6;
  return a[1] ;
}

/**
 * @param {string} str97
 * @remark 数组成员只能用方括号arr[0]表示（方括号是运算符，可以接受数值）。
 */
// function first74(str97) {
//   var arr = [1, 2, 3];
//   return arr.0;
// }
// console.log(first71())//object
// console.log(first72())//[ '0', '1' ]
// console.log(first73())//6
// console.log(first74())//SyntaxError: Unexpected number

//2.7.3
/**
 * @param {string} str98
 * @remark 数组的length属性，返回数组的成员数量。
 * @remark length2:arr[0]返回的是数组的第一个元素。
 */
function first75(str98) {
  var arr = [1, 2];
  return{
    length:arr.length ,
    length2:arr[0]
  }
}

/**
 * @param {string} str99
 * @remark 该属性是一个动态的值，等于键名中的最大整数加上1。
 */
function first76(str99) {
  var arr = [1, 2];
  // arr[3] = 4;
  return{
    length:arr.length ,
    length2:arr[1]
  }
}

/**
 * @param {string} str100
 * @remark length属性是可写的。如果人为设置一个小于当前成员个数的值，该数组的成员数量会自动减少到length设置的值。
 */
function first77(str100) {
  var arr = [1, 2,3,4];
  arr.length = 3;
  return arr;
}

/**
 * @param {string} str101
 * @remark 由于数组本质上是一种对象，所以可以为数组添加属性，但是这不影响length属性的值。
 * @remark 数组的键名是添加超出范围的数值，该键名会自动转为字符串。
 */
function first78(str101) {
  var a = [];
  a['p'] = 'abc';
  a[-11] = 2;
  return {
    return1:a.length,
    return2:a['p'],
    return3:a[-11]
  }
}

// console.log(first75())//{ length: 2, length2: 1 }
// console.log(first76())//{ length: 4, length2: 2 }
// console.log(first77())//[ 1, 2, 3 ]
// console.log(first78())//{ return1: 3, return2: 'abc', return3: 2 }

//2.7.4
/**
 * @param {string} str102
 * @remark 检查某个键名是否存在的运算符in，适用于对象，也适用于数组。
 * @remark 由于键名都是字符串，所以数值2会自动转成字符串。so,'0' in arr和0 in arr都表示数组是否存在该键名。
 */
function first79(str102) {
  var arr = [ 'a', 'b', 'c' ];
  return {
    return1:0 in arr,
    return2:'0' in arr, 
    return3:4 in arr
  }
}

/**
 * @param {string} str103
 * @remark 如果数组的某个位置是空位，in运算符返回false。
 */
function first80(str103) {
  var arr = [];
  arr[100] = 'a';  
  return{
    return1:100 in arr ,
    return2:1 in arr, 
    return3:arr.length
  }
}
// console.log(first79())//{ return1: true, return2: true, return3: false }
// console.log(first80())//{ return1: true, return2: false, return3: 101 }

//2.7.5
/**
 * @param {string} str104
 * @remark for...in循环不仅可以遍历对象，也可以遍历数组，毕竟数组只是一种特殊对象。
 */
function first81(str104) {
  var arr = [ 'a', 'b', 'c' ];
  for ( var i in arr ) {
    console.log('>>>print', i, arr[i])
  }
  return arr;
}

/**
 * @param {string} str105
 * @remark for...in不仅会遍历数组所有的数字键，还会遍历非数字键。
 */
function first82(str105) {
  var arr = [ 'a', 'b', 'c' ];
  arr.d = 4;
  for ( var i in arr ) {
    console.log('>>>print', i, arr[i])
  }
  return arr;
}

/**
 * @param {string} str106
 * @remark 数组的遍历可以考虑使用for循环或while循环。
 */
function first83(str106) {
  var arr = [ 'a', 'b', 'c' ];
  var i=0;
  while ( i < arr.length ) {
    console.log('>>>print', i, arr[i])
    ++i;
  }
  return arr;
}

/**
 * @param {string} str107
 * @remark 数组的forEach方法，也可以用来遍历数组，
 */
function first84(str107) {
  var arr = [ 'a', 'b', 'c' ];
  arr.forEach(function (item, index) {
    console.log('>>>print', index, item);
  })
  return arr;
}
// console.log(first81())
/*
>>>print 0 a
>>>print 1 b
>>>print 2 c
[ 'a', 'b', 'c' ]
*/
// console.log(first82())
/*
>>>print 0 a
>>>print 1 b
>>>print 2 c
>>>print d 4
[ 'a', 'b', 'c', d: 4 ]
*/
// console.log(first83())
/*
>>>print 0 a
>>>print 1 b
>>>print 2 c
[ 'a', 'b', 'c' ]
*/
// console.log(first84())
/*
>>>print 0 a
>>>print 1 b
>>>print 2 c
[ 'a', 'b', 'c' ]
*/

//2.7.6
/**
 * @param {string} str108
 * @remark 当数组的某个位置是空元素，即两个逗号之间没有任何值，我们称该数组存在空位（hole）
 */
function first85(str108) {
  var a = [1, , 1];
  return a.length
}

/**
 * @param {string} str109
 * @remark 如果最后一个元素后面有逗号，并不会产生空位。也就是说，有没有这个逗号，结果都是一样的。
 */
function first86(str109) {
  var a = [1, 2, 3,];
  return a
}

/**
 * @param {string} str108
 * @remark 数组的空位是可以读取的，返回undefined
 */
function first87(str108) {
  var a = [, ,];
  return a[1]
}

/**
 * @param {string} str108
 * @remark 使用delete命令删除一个数组成员，会形成空位，并且不会影响length属性。
 */
function first88(str108) {
  var a = [1,2 ,3];
  delete a[1]
  return {
    return1:a.length,
    return2:a[1]
  }
}

/**
 * @param {string} str108
 * @remark 数组的某个位置是空位，与某个位置是undefined，是不一样的。如果是空位，使用数组的forEach方法、for...in结构、以及Object.keys方法进行遍历，空位都会被跳过。
 */
function first89(str108) {
  var a = [, ,];
  a.forEach(function (item, index) {
    console.log('>>>print', item, index);
  })
  return a
}
function first90(str108) {
  var a = [undefined, undefined,undefined];
  for ( var i in a ) {
    console.log('>>>print', i, a[i])
  }
  return a
}
// console.log(first85())//3
// console.log(first86())//[1, 2, 3]
// console.log(first87())//undefined
// console.log(first88())//{ return1: 3, return2: undefined }
// console.log(first89())//[ <2 empty items> ]
// console.log(first90())
/*
>>>print 0 undefined
>>>print 1 undefined
>>>print 2 undefined
[ undefined, undefined, undefined ]
*/

//2.7.7
/**
 * @param {string} str108
 * @remark 如果一个对象的所有键名都是正整数或零，并且有length属性，那么这个对象就很像数组，语法上称为“类似数组的对象”（array-like object）。
 */
function first91(str108) {
  var obj = {
    0: 'a',
    1: 'b',
    2: 'c',
    length: 4
  }
  return {
    return1:obj.length,
    return2:obj[3]
  } 
}

/**
 * @param {string} str108
 * @remark 这种length属性不是动态值，不会随着成员的变化而变化。
 */
function first92(str108) {
  var obj = {
    length: 4
  }
  obj[3] = 'd'
  return obj.length
}

/**
 * @param {string} str108
 * @remark 典型的“类似数组的对象”是函数的arguments对象，以及大多数 DOM 元素集，还有字符串。
 */
function first93(str108) {
  function args() { return arguments }
  var arrayLike = args('a', 'b');
  return {
    return1:arrayLike.length,
    return2:arrayLike[3]
  }
}

/**
 * @param {string} str108
 * @remark 数组的slice方法可以将“类似数组的对象”变成真正的数组。
 */
function first94(str108) {
  var arrayLike = { 0: 'a', 1: 'b', length: 2 };
  var result = Array.prototype.slice.call(arrayLike, 0);
  return result
}

/**
 * @param {string} str108
 * @remark 使用数组的方法，就是通过call()把数组的方法放到对象上面。
 */
function first95(str108) {
  var arrayLike = {
    0: 10,
    1: 20,
    2: 30,
    length: 3
  }
  function print(value, index) {
    console.log(index + ' : ' + value);
  }
  Array.prototype.forEach.call(arrayLike, print);
}
// console.log(first91())//{ return1: 4, return2: undefined }
// console.log(first92())//4
// console.log(first93())//{ return1: 2, return2: undefined }
// console.log(first94())//[ 'a', 'b' ]
// console.log(first95())
/*
0 : 10
1 : 20
2 : 30
undefined
*/